{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "bad43e51",
   "metadata": {},
   "source": [
    "# JsonOutputParser\n",
    "\n",
    "JsonOutputParser는 사용자가 원하는 JSON 스키마를 지정할 수 있게 해주는 도구입니다. 이 도구는 Large Language Model (LLM)이 데이터를 조회하고 결과를 도출할 때, 지정된 스키마에 맞게 JSON 형식으로 데이터를 반환할 수 있도록 설계되었습니다.\n",
    "\n",
    "LLM이 데이터를 정확하고 효율적으로 처리하여 사용자가 원하는 형태의 JSON을 생성하기 위해서는, 모델의 용량(예: 인텔리전스)이 충분히 커야 합니다. 예를 들어, llama-70B 모델은 llama-8B 모델보다 더 큰 용량을 가지고 있어 보다 복잡한 데이터를 처리하는 데 유리합니다.\n",
    "\n",
    "**[참고]**\n",
    "\n",
    "`JSON (JavaScript Object Notation)` 은 데이터를 저장하고 구조적으로 전달하기 위해 사용되는 경량의 데이터 교환 포맷입니다. 웹 개발에서 매우 중요한 역할을 하며, 서버와 클라이언트 간의 통신을 위해 널리 사용됩니다. JSON은 읽기 쉽고, 기계가 파싱하고 생성하기 쉬운 텍스트를 기반으로 합니다.\n",
    "\n",
    "JSON의 기본 구조\n",
    "JSON 데이터는 이름(키)과 값의 쌍으로 이루어져 있습니다. 여기서 \"이름\"은 문자열이고, \"값\"은 다양한 데이터 유형일 수 있습니다. JSON은 두 가지 기본 구조를 가집니다:\n",
    "\n",
    "- 객체: 중괄호 {}로 둘러싸인 키-값 쌍의 집합입니다. 각 키는 콜론 :을 사용하여 해당하는 값과 연결되며, 여러 키-값 쌍은 쉼표 ,로 구분됩니다.\n",
    "- 배열: 대괄호 []로 둘러싸인 값의 순서 있는 목록입니다. 배열 내의 값은 쉼표 ,로 구분됩니다.\n",
    "\n",
    "```json\n",
    "{\n",
    "  \"name\": \"John Doe\",\n",
    "  \"age\": 30,\n",
    "  \"is_student\": false,\n",
    "  \"skills\": [\"Java\", \"Python\", \"JavaScript\"],\n",
    "  \"address\": {\n",
    "    \"street\": \"123 Main St\",\n",
    "    \"city\": \"Anytown\"\n",
    "  }\n",
    "}\n",
    "```"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "0a77ca57",
   "metadata": {},
   "outputs": [],
   "source": [
    "from dotenv import load_dotenv\n",
    "\n",
    "load_dotenv()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "98a0bac6",
   "metadata": {},
   "outputs": [],
   "source": [
    "# LangSmith 추적을 설정합니다. https://smith.langchain.com\n",
    "# !pip install langchain-teddynote\n",
    "from langchain_teddynote import logging\n",
    "\n",
    "# 프로젝트 이름을 입력합니다.\n",
    "logging.langsmith(\"CH03-OutputParser\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "f2dea9c0",
   "metadata": {},
   "outputs": [],
   "source": [
    "from langchain_core.prompts import ChatPromptTemplate\n",
    "from langchain_core.output_parsers import JsonOutputParser\n",
    "from langchain_openai import ChatOpenAI\n",
    "from pydantic import BaseModel, Field"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "e4b1465b",
   "metadata": {},
   "outputs": [],
   "source": [
    "# OpenAI 객체를 생성합니다.\n",
    "model = ChatOpenAI(temperature=0, model_name=\"gpt-4.1-mini\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c90d3bdd",
   "metadata": {},
   "source": [
    "원하는 출력 구조를 정의합니다.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "40f59601",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 원하는 데이터 구조를 정의합니다.\n",
    "class Topic(BaseModel):\n",
    "    description: str = Field(description=\"주제에 대한 간결한 설명\")\n",
    "    hashtags: str = Field(description=\"해시태그 형식의 키워드(2개 이상)\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b584159b",
   "metadata": {},
   "source": [
    "`JsonOutputParser`를 사용하여 파서를 설정하고, 프롬프트 템플릿에 지시사항을 주입합니다."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "d8adf21d",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 질의 작성\n",
    "question = \"지구 온난화의 심각성 대해 알려주세요.\"\n",
    "\n",
    "# 파서를 설정하고 프롬프트 템플릿에 지시사항을 주입합니다.\n",
    "parser = JsonOutputParser(pydantic_object=Topic)\n",
    "print(parser.get_format_instructions())"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "dacd3dd3",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 프롬프트 템플릿을 설정합니다.\n",
    "prompt = ChatPromptTemplate.from_messages(\n",
    "    [\n",
    "        (\"system\", \"당신은 친절한 AI 어시스턴트 입니다. 질문에 간결하게 답변하세요.\"),\n",
    "        (\"user\", \"#Format: {format_instructions}\\n\\n#Question: {question}\"),\n",
    "    ]\n",
    ")\n",
    "\n",
    "prompt = prompt.partial(format_instructions=parser.get_format_instructions())\n",
    "\n",
    "# 체인을 구성합니다.\n",
    "chain = prompt | model | parser\n",
    "\n",
    "# 체인을 호출하여 쿼리 실행\n",
    "answer = chain.invoke({\"question\": question})"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "2e7fbbd6",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 타입을 확인합니다.\n",
    "type(answer)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "472ecbaf",
   "metadata": {},
   "outputs": [],
   "source": [
    "# answer 객체를 출력합니다.\n",
    "answer"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c8abbbbd",
   "metadata": {},
   "source": [
    "**Pydantic 을 사용하지 않고 `JsonOutputParser` 를 사용**\n",
    "\n",
    "Pydantic 없이도 이 기능을 사용할 수 있습니다. 이 경우 JSON을 반환하도록 요청하지만, 스키마가 어떻게 되어야 하는지에 대한 구체적인 정보는 제공하지 않습니다."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "3c3a2ce1",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 질의 작성\n",
    "question = \"지구 온난화에 대해 알려주세요. 온난화에 대한 설명은 `description`에, 관련 키워드는 `hashtags`에 담아주세요.\"\n",
    "\n",
    "# JSON 출력 파서 초기화\n",
    "parser = JsonOutputParser()\n",
    "\n",
    "# 프롬프트 템플릿을 설정합니다.\n",
    "prompt = ChatPromptTemplate.from_messages(\n",
    "    [\n",
    "        (\"system\", \"당신은 친절한 AI 어시스턴트 입니다. 질문에 간결하게 답변하세요.\"),\n",
    "        (\"user\", \"#Format: {format_instructions}\\n\\n#Question: {question}\"),\n",
    "    ]\n",
    ")\n",
    "\n",
    "# 지시사항을 프롬프트에 주입합니다.\n",
    "prompt = prompt.partial(format_instructions=parser.get_format_instructions())\n",
    "\n",
    "# 프롬프트, 모델, 파서를 연결하는 체인 생성\n",
    "chain = prompt | model | parser\n",
    "\n",
    "# 체인을 호출하여 쿼리 실행\n",
    "response = chain.invoke({\"question\": question})\n",
    "\n",
    "# 출력을 확인합니다.\n",
    "print(response)"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "py-test",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.11.9"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
